#include <Sieve.hpp>
#include <iostream>
#include <Trie.hpp>
#include <sstream>
#include <Utility.hpp>
#include <cstdlib>
#include <ctime>
#include <GTrie.hpp>
#include <mpi.h>
#include <list>
#include <vector>

Sieve::Sieve(int rank, int np, int ext) : 
rank(rank), np(np), ext(ext) {
	// nothing
}

void Sieve::run() {

	int vtime;

	MPI::COMM_WORLD.Bcast(&vtime, 1, MPI_INT, 0);

	srand(vtime + rank);


	int bsz;
	// MPI::COMM_WORLD.Recv(&bsz, 1, MPI_INT, 0, MPI_ANY_TAG);
	MPI::COMM_WORLD.Bcast(&bsz, 1, MPI_INT, 0);

	char * buffer = new char[bsz+1];
	buffer[bsz] = 0;
	MPI::COMM_WORLD.Bcast(buffer, bsz, MPI_CHAR, 0);

	// MPI::Status status;
	// for (int st = 0; st < bsz; ) {
	// 	MPI::COMM_WORLD.Recv(buffer, bsz,
	// 		MPI_CHAR, 0, MPI_ANY_TAG, status);
	// 	st += status.Get_count(MPI_CHAR);
	// }

	std::stringstream ss;
	ss << std::string(buffer);
	delete buffer;

	Trie * trie = new Trie();
	int myqty = trie->deserialize(ss);

	std::cerr << "Sieve " << rank << " received "
		<< myqty << " parts\n";

	int tryc = 0;
	Trie * foundTrie = new Trie();

	
	for (int found = 0;; ) {

		if (tryc == ext || found >= myqty) {
	
			tryc = 0;

			std::stringstream ss;
			foundTrie->serialize(ss);
			std::string str;
			ss >> str;
			int sz = str.size();
			buffer = new char[sz+1];

			buffer[sz] = 0;
			for (int i = 0; i < sz; i++)
				buffer[i] = str[i];

			MPI::COMM_WORLD.Send(&sz, 1, MPI_INT, 0, 1);
			MPI::COMM_WORLD.Send(buffer, sz, MPI_CHAR, 0, 1);
			delete buffer;

			int bsz;
			MPI::COMM_WORLD.Bcast(&bsz, 1, MPI_INT, 0);
			
			buffer = new char[bsz+1];
			buffer[bsz] = 0;
			MPI::COMM_WORLD.Bcast(buffer, bsz, MPI_CHAR, 0);

			std::stringstream ss2;
			ss2 << std::string(buffer);
			delete buffer;
			foundTrie->deserialize(ss2);

			std::list<std::string> list;
			foundTrie->itemize(list);
			std::list<std::string>::iterator it, itEnd;
			it = list.begin();
			itEnd = list.end();
			
			int helped = 0;
			for (; it != itEnd; it++) {
				std::vector<std::string> search = 
					trie->retrieve(*it);
				if (search.size()) {
					helped ++;
					found++;
					trie->remove(*it);
				}
			}
			std::cerr << "Sieve " << rank << 
				" helped " << helped << 
				" - " << found << "\n"; 


			delete foundTrie;
			foundTrie = new Trie();

			if (found >= myqty) break;
		}

		std::string word = Utility::randomWord(true);
		std::vector<std::string> search = 
			trie->retrieveUntil(word);
		
		tryc++;
		if (search.size()) {
			found++;
			trie->remove(word);
			foundTrie->insert(word);
		}
	}

	delete foundTrie;
	delete trie;

	std::cerr << "Sieve " << rank << 
		" finished. Waiting for others\n";

	MPI::COMM_WORLD.Barrier();

}

